<script setup lang='ts'>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { NInput, NCard, NSpace, NButton, NDrawer, NDrawerContent, NList, NListItem, NPagination, NSelect, NTag, NSpin } from 'naive-ui'
import { useMessage } from 'naive-ui'

const message = useMessage()

const baseApi = 'http://192.168.1.5:5000/api/logs'

const hosts = ref<string[]>([])
const dates = ref<string[]>([])
const files = ref<string[]>([])

const selectedHost = ref('')
const selectedDate = ref('')
const selectedFile = ref('')

const searchValue = ref('')
const levelFilters = ref<string[]>([])
const levelOptions = [
  { label: 'CRITICAL', value: 'CRITICAL' },
  { label: 'ERROR', value: 'ERROR' },
  { label: 'WARN', value: 'WARN' },
  { label: 'INFO', value: 'INFO' },
  { label: 'DEBUG', value: 'DEBUG' }
]

const currentPage = ref(1)
const pageSize = ref(500)
const totalLines = ref(0)
const lines = ref<string[]>([])
const loading = ref(false)

const autoRefreshTimer = ref<number | null>(null)

// Drawer
const showDetailDrawer = ref(false)

// AI分析相关状态
const analyzingFiles = ref(new Set<string>())
const analysisResults = ref<Map<string, string>>(new Map())
const showAnalysisDrawer = ref(false)
const currentAnalysisFile = ref('')
const currentAnalysisResult = ref('')

// 检查文件是否已有分析报告
const checkAnalysisExists = async (fileName: string): Promise<boolean> => {
  try {
    const response = await fetch(`${baseApi}/analysis?filename=${encodeURIComponent(fileName)}`)
    return response.ok
  } catch (error) {
    console.warn(`检查分析报告失败 (${fileName}):`, error)
    return false
  }
}

// 从后端获取分析报告内容
const fetchAnalysisResult = async (fileName: string): Promise<string> => {
  try {
    console.log(`正在获取分析报告: ${fileName}`)
    const response = await fetch(`${baseApi}/analysis?filename=${encodeURIComponent(fileName)}`)
    console.log(`分析报告API响应状态: ${response.status}`)
    
    if (response.ok) {
      const data = await response.json()
      console.log(`分析报告API响应数据:`, data)
      if (data.status === 'success') {
        return data.analysis_result || ''
      } else {
        console.warn(`获取分析报告失败: ${data.message}`)
        message.error(`获取分析报告失败: ${data.message}`)
      }
    } else {
      const errorText = await response.text()
      console.warn(`获取分析报告HTTP错误: ${response.status} - ${errorText}`)
      message.error(`获取分析报告失败: HTTP ${response.status}`)
    }
  } catch (error) {
    console.warn(`获取分析报告失败 (${fileName}):`, error)
    message.error(`获取分析报告失败: ${error}`)
  }
  return ''
}

// 级别识别：与后端一致的词匹配，提升过滤准确性
const severityPatterns: Array<[RegExp, string]> = [
  [/\b(critical|crit|fatal|panic)\b/i, 'CRITICAL'],
  [/\b(error|err|fail|failed|exception|traceback)\b/i, 'ERROR'],
  [/\b(warn|warning|deprecated)\b/i, 'WARN'],
  [/\b(info|information|started|listening|connected)\b/i, 'INFO'],
  [/\b(debug|trace)\b/i, 'DEBUG']
]

const classifyLine = (line: string): string => {
  for (const [re, lv] of severityPatterns) {
    if (re.test(line)) return lv
  }
  return 'INFO'
}

const decoratedLines = computed(() => {
  return filteredLines.value.map((text, idx) => {
    const level = classifyLine(text)
    return { index: idx + 1 + (currentPage.value - 1) * pageSize.value, level, text }
  })
})

// 过滤文件列表
const filteredFiles = computed(() => {
  let data = files.value
  // 根据文件名搜索过滤
  if (searchValue.value.trim()) {
    const term = searchValue.value.toLowerCase()
    data = data.filter(f => f.toLowerCase().includes(term))
  }
  return data
})

const filteredLines = computed(() => {
  let data = lines.value
  // 应用级别过滤
  if (levelFilters.value.length) {
    data = data.filter(l => levelFilters.value.includes(classifyLine(l)))
  }
  return data
})

const onSearch = async () => {
  // 搜索功能：触发文件列表过滤，无需重新获取内容
  // 搜索功能现在是实时过滤文件名，不需要额外的操作
}

// 清空所有过滤器
const clearFilters = () => {
  searchValue.value = ''
  levelFilters.value = []
}

// AI分析日志文件
const analyzeLog = async (filePath: string, fileName: string) => {
  analyzingFiles.value.add(fileName)
  try {
    const response = await fetch(`${baseApi}/analyze`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        file_path: filePath
      })
    })
    
    const result = await response.json()
    if (result.status === 'success') {
      // 分析结果已保存到后端logsanalysis目录，这里只缓存到内存用于当前显示
      analysisResults.value.set(fileName, result.analysis_result)
      message.success('智能分析完成')
      // 自动打开分析结果
      currentAnalysisFile.value = fileName
      currentAnalysisResult.value = result.analysis_result
      showAnalysisDrawer.value = true
    } else {
      message.error(result.message || '智能分析失败')
    }
  } catch (error) {
    message.error('智能分析请求失败')
    console.error('AI分析错误:', error)
  } finally {
    analyzingFiles.value.delete(fileName)
  }
}

// 查看分析结果
const viewAnalysis = async (fileName: string) => {
  console.log(`查看分析结果: ${fileName}`)
  // 直接从后端获取分析结果
  const result = await fetchAnalysisResult(fileName)
  
  if (result) {
    // 缓存到内存中用于当前显示
    analysisResults.value.set(fileName, result)
    currentAnalysisFile.value = fileName
    currentAnalysisResult.value = result
    showAnalysisDrawer.value = true
    console.log(`成功获取分析结果，长度: ${result.length}`)
  } else {
    console.warn(`无法获取分析结果: ${fileName}`)
    message.error(`无法获取分析结果: ${fileName}`)
  }
}

// 简单的Markdown格式化函数
const formatMarkdown = (text: string) => {
  if (!text) return ''
  
  let html = text
    // 标题处理
    .replace(/^### (.*$)/gim, '<h3>$1</h3>')
    .replace(/^## (.*$)/gim, '<h2>$1</h2>')
    .replace(/^# (.*$)/gim, '<h1>$1</h1>')
    // 粗体处理
    .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
    // 代码块处理
    .replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>')
    // 行内代码处理
    .replace(/`(.*?)`/g, '<code>$1</code>')
    // 列表处理
    .replace(/^\d+\. (.*$)/gim, '<li>$1</li>')
    .replace(/^- (.*$)/gim, '<li>$1</li>')
    // 换行处理
    .replace(/\n/g, '<br>')
  
  // 包装列表项
  html = html.replace(/(<li>.*<\/li>)/gs, '<ul>$1</ul>')
  
  return html
}

const fetchTree = async () => {
  try {
    const res = await fetch(`${baseApi}/list`)
    const data = await res.json()
    console.log('API响应:', data) // 调试信息
    
    const tree = data.tree || {}
    console.log('解析的树结构:', tree) // 调试信息
    
    hosts.value = Object.keys(tree)
    console.log('主机列表:', hosts.value) // 调试信息
    
    if (hosts.value.length) {
      selectedHost.value = hosts.value.sort()[hosts.value.length - 1]
      const hostNode = tree[selectedHost.value] || {}
      dates.value = Object.keys(hostNode)
      console.log('日期列表:', dates.value) // 调试信息
      
      if (dates.value.length) {
        selectedDate.value = dates.value.sort()[dates.value.length - 1]
        const dateNode = hostNode[selectedDate.value] || {}
        files.value = (dateNode.files || [])
        console.log('文件列表:', files.value) // 调试信息
        
        if (files.value.length) {
          const preferred = files.value.find((f: string) => f.endsWith('.log')) || files.value[0]
          selectedFile.value = `${selectedHost.value}/${selectedDate.value}/${preferred}`.replace(/\\/g, '/')
        }
      }
    } else {
      console.log('没有找到主机目录') // 调试信息
      message.warning('没有找到日志文件，请先执行日志采集')
    }
  } catch (e) {
    console.error('获取日志目录失败:', e) // 调试信息
    message.error('获取日志目录失败')
  }
}

const fetchLatest = async () => {
  try {
    const res = await fetch(`${baseApi}/latest`)
    const data = await res.json()
    console.log('最新日志API响应:', data) // 调试信息
    
    if (data && data.host && data.date && data.files && data.files.length) {
      selectedHost.value = data.host
      selectedDate.value = data.date
      const preferred = data.files.find((p: string) => p.endsWith('.log')) || data.files[0]
      selectedFile.value = preferred
      console.log('设置最新文件:', selectedFile.value) // 调试信息
    } else {
      console.log('没有最新日志文件') // 调试信息
    }
  } catch (e) {
    console.error('获取最新日志失败:', e) // 调试信息
  }
}

const fetchContent = async (tail = false) => {
  if (!selectedFile.value) return
  loading.value = true
  try {
    const params = new URLSearchParams({
      path: selectedFile.value,
      page: String(currentPage.value),
      page_size: String(pageSize.value),
      tail: String(tail)
    })
    const res = await fetch(`${baseApi}/content?${params.toString()}`)
    const data = await res.json()
    lines.value = data.lines || []
    totalLines.value = data.total_lines || 0
    if (tail) {
      const lastPage = Math.max(1, Math.ceil((data.total_lines || 0) / pageSize.value))
      currentPage.value = lastPage
    }
  } catch (e) {
    message.error('获取日志内容失败')
  } finally {
    loading.value = false
  }
}

const refresh = async () => {
  await fetchTree()
  if (selectedFile.value) {
    await fetchContent(true)
  } else {
    await fetchLatest()
    await fetchContent(true)
  }
}

const handlePageChange = async (page: number) => {
  currentPage.value = page
  await fetchContent(false)
}

const openFile = async (fileRelPath: string) => {
  selectedFile.value = fileRelPath
  currentPage.value = 1
  await fetchContent(true)
  showDetailDrawer.value = true
}

const startAuto = () => {
  stopAuto()
  autoRefreshTimer.value = window.setInterval(() => {
    fetchContent(true)
  }, 10000)
}

const stopAuto = () => {
  if (autoRefreshTimer.value) {
    window.clearInterval(autoRefreshTimer.value)
    autoRefreshTimer.value = null
  }
}

onMounted(async () => {
  await refresh()
  await fetchStorageInfo()
  startAuto()
})

onUnmounted(() => {
  stopAuto()
})

const fileDisplayName = computed(() => selectedFile.value.split('/').slice(-1)[0] || '')

// 新增：日志采集相关状态
const collecting = ref(false)
const collectionMode = ref('default') // default | path_list
const collectionConfig = ref({
  min_level: 'WARN',
  since_hours: 24,
  max_lines_per_source: 10000,
  max_total_lines: 50000,
  incremental: true,
  save_raw: false
})

// 新增：存储信息状态
const storageInfo = ref<any>(null)

// 新增：获取存储信息
const fetchStorageInfo = async () => {
  try {
    const response = await fetch(`${baseApi}/storage-info`)
    if (response.ok) {
      const data = await response.json()
      if (data.status === 'success') {
        storageInfo.value = data.storage_info
      }
    }
  } catch (error) {
    console.warn('获取存储信息失败:', error)
  }
}

// 新增：手动触发日志采集
const collectLogs = async () => {
  collecting.value = true
  try {
    const response = await fetch(`${baseApi}/collect`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        mode: collectionMode.value,
        ...collectionConfig.value
      })
    })
    
    const result = await response.json()
    console.log('采集响应:', result) // 调试信息
    
    if (result.status === 'success') {
      message.success('日志采集完成')
      // 更新存储信息
      if (result.storage_info) {
        storageInfo.value = result.storage_info
      }
      // 刷新日志列表
      await refresh()
    } else {
      message.error(result.message || '日志采集失败')
    }
  } catch (error) {
    message.error('日志采集请求失败')
    console.error('日志采集错误:', error)
  } finally {
    collecting.value = false
  }
}

// 新增：启动定时采集
const startScheduledCollection = async () => {
  try {
    const response = await fetch(`${baseApi}/schedule`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        interval: 300, // 5分钟
        ...collectionConfig.value
      })
    })
    
    const result = await response.json()
    if (result.status === 'success' || result.status === 'started' || result.status === 'restarted') {
      message.success('定时采集已启动')
      if (result.storage_info) {
        storageInfo.value = result.storage_info
      }
    } else {
      message.error(result.message || '启动定时采集失败')
    }
  } catch (error) {
    message.error('启动定时采集请求失败')
    console.error('定时采集错误:', error)
  }
}

// 新增：停止定时采集
const stopScheduledCollection = async () => {
  try {
    const response = await fetch(`${baseApi}/stop`, {
      method: 'POST'
    })
    
    const result = await response.json()
    if (result.status === 'stopped') {
      message.success('定时采集已停止')
    } else {
      message.info('定时采集未运行')
    }
  } catch (error) {
    message.error('停止定时采集请求失败')
    console.error('停止定时采集错误:', error)
  }
}
</script>

<template>
  <div class="container">
    <!-- 顶部操作栏 -->
    <div class="search-box mb-4">
      <n-input v-model:value="searchValue" type="text" placeholder="搜索日志文件名..." clearable style="width: 50%" />
      <n-select v-model:value="levelFilters" :options="levelOptions" multiple placeholder="按级别过滤" style="width: 260px; margin-left: 8px;" />
      <n-button type="primary" @click="onSearch" class="ml-2">搜索</n-button>
      <n-button type="primary" @click="refresh" class="ml-2">刷新</n-button>
      <n-button type="primary" class="ml-2" @click="clearFilters">清空过滤</n-button>
    </div>

    <!-- 日志采集控制栏 -->
    <n-card title="日志采集控制" size="small" class="mb-4">
      <n-space>
        <n-select 
          v-model:value="collectionMode" 
          :options="[
            { label: '本地采集', value: 'default' },
            { label: '文件列表采集', value: 'path_list' }
          ]" 
          style="width: 150px"
        />
        <n-select 
          v-model:value="collectionConfig.min_level" 
          :options="levelOptions" 
          placeholder="最低级别" 
          style="width: 120px"
        />
        <n-input 
          v-model:value="collectionConfig.since_hours" 
          type="number" 
          placeholder="时间范围(小时)" 
          style="width: 150px"
        />
        <n-button 
          type="primary" 
          @click="collectLogs" 
          :loading="collecting"
        >
          立即采集
        </n-button>
        <n-button type="success" @click="startScheduledCollection">
          启动定时采集
        </n-button>
        <n-button type="warning" @click="stopScheduledCollection">
          停止定时采集
        </n-button>
      </n-space>
    </n-card>

    <!-- 存储信息显示 -->
    <n-card v-if="storageInfo" title="存储信息" size="small" class="mb-4">
      <n-space>
        <n-tag type="info">日志目录: {{ storageInfo.logs_dir }}</n-tag>
        <n-tag type="success">分析目录: {{ storageInfo.analysis_dir }}</n-tag>
        <n-tag :type="storageInfo.logs_dir_exists === 'True' ? 'success' : 'error'">
          日志目录: {{ storageInfo.logs_dir_exists === 'True' ? '存在' : '不存在' }}
        </n-tag>
        <n-tag :type="storageInfo.analysis_dir_exists === 'True' ? 'success' : 'error'">
          分析目录: {{ storageInfo.analysis_dir_exists === 'True' ? '存在' : '不存在' }}
        </n-tag>
      </n-space>
    </n-card>

    <!-- 主容器：文件卡片列表，点击"查看"打开 Drawer 预览内容 -->
    <n-card :title="`日志文件 (${filteredFiles.length}/${files.length})`" class="main-container">
      <!-- 调试信息显示 -->
      <div v-if="files.length === 0" class="debug-info">
        <p>调试信息:</p>
        <p>主机: {{ selectedHost }}</p>
        <p>日期: {{ selectedDate }}</p>
        <p>文件数量: {{ files.length }}</p>
        <p>主机列表: {{ hosts }}</p>
        <p>日期列表: {{ dates }}</p>
        <p>过滤后文件: {{ filteredFiles }}</p>
        <n-button type="primary" @click="refresh" size="small">重新获取</n-button>
      </div>
      <n-space vertical>
        <n-card
          v-for="f in filteredFiles"
          :key="f"
          class="item-box"
          hoverable
        >
          <div class="flex justify-between items-center">
            <div class="file-info">
              <span class="file-name">{{ f }}</span>
              <n-tag type="info" size="small">日期: {{ selectedDate }}</n-tag>
            </div>
            <n-space>
              <n-button type="primary" size="small" @click="openFile(`${selectedHost}/${selectedDate}/${f}`.replace(/\\\\/g, '/'))">
                查看日志
              </n-button>
              <n-button 
                type="success" 
                size="small" 
                @click="analyzeLog(`${selectedHost}/${selectedDate}/${f}`.replace(/\\\\/g, '/'), f)" 
                :loading="analyzingFiles.has(f)"
              >
                AI分析
              </n-button>
              <n-button 
                type="warning" 
                size="small" 
                @click="viewAnalysis(f)"
              >
                查看结果
              </n-button>
            </n-space>
          </div>
        </n-card>
      </n-space>
    </n-card>

    <!-- Drawer 预览详情 -->
    <n-drawer v-model:show="showDetailDrawer" :width="'80%'" placement="right">
      <n-drawer-content closable>
        <template #header>
          <h3>{{ fileDisplayName }}</h3>
        </template>

        <n-card size="small">
          <n-space :size="8" align="center">
            <n-tag type="info">日期</n-tag>
            <n-input v-model:value="selectedDate" disabled style="width: 160px" />
            <n-tag type="info">文件</n-tag>
            <n-input v-model:value="fileDisplayName" disabled style="width: 260px" />
            <n-button type="primary" @click="() => fetchContent(true)">定位尾部</n-button>
          </n-space>
        </n-card>

        <n-card :title="'内容预览'" size="small" style="margin-top: 10px;">
          <n-spin :show="loading">
            <div class="log-view">
              <div v-for="row in decoratedLines" :key="row.index" class="log-row" :class="'lv-' + row.level.toLowerCase()">
                <span class="ln">{{ row.index }}</span>
                <span class="txt">{{ row.text }}</span>
              </div>
            </div>
          </n-spin>
          <div class="pagination-container">
            <n-pagination 
              v-model:page="currentPage" 
              :page-size="pageSize" 
              :item-count="totalLines" 
              @update:page="handlePageChange"
            />
          </div>
        </n-card>
      </n-drawer-content>
    </n-drawer>

    <!-- Drawer 显示分析结果 -->
    <n-drawer v-model:show="showAnalysisDrawer" :width="'80%'" placement="right">
      <n-drawer-content closable>
        <template #header>
          <h3>{{ currentAnalysisFile }} - 智能分析结果</h3>
        </template>

        <n-card :title="'分析结果'" size="small">
          <div class="analysis-result" v-html="formatMarkdown(currentAnalysisResult)"></div>
        </n-card>
      </n-drawer-content>
    </n-drawer>
  </div>
</template>

<style lang='scss' scoped>
.container {
  padding: 20px;
}
.toolbar {
  margin-bottom: 12px;
}
.search-box {
  display: flex;
  align-items: center;
}
.row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.label {
  color: #666;
}
.input {
  height: 32px;
  padding: 0 8px;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
}
.select {
  min-width: 180px;
}
.btn {
  height: 32px;
  padding: 0 12px;
  border: 1px solid #e5e7eb;
  background: #fff;
  border-radius: 4px;
  cursor: pointer;
}
.btn.primary {
  background: #5CADFF;
  color: #fff;
  border-color: #5CADFF;
}
.btn.link {
  border: none;
  background: transparent;
  color: #5CADFF;
  cursor: pointer;
}
.content {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 12px;
}
.sidebar {
  max-height: 70vh;
  overflow: auto;
}
.viewer {
  max-height: 70vh;
  display: flex;
  flex-direction: column;
}
.card {
  border: 1px solid #e5e7eb;
  border-radius: 6px;
}
.card-title {
  padding: 10px;
  border-bottom: 1px solid #e5e7eb;
  font-weight: 600;
}
.file-list {
  list-style: none;
  padding: 10px;
}
.file-list li {
  margin-bottom: 4px;
}
.log-view {
  white-space: pre-wrap;
  word-wrap: break-word;
  background-color: #0f172a;
  color: #e2e8f0;
  padding: 10px;
  border-radius: 4px;
  height: 58vh;
  overflow-y: auto;
}
.log-row {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 12px;
  align-items: start;
}
.log-row .ln {
  color: #94a3b8;
  text-align: right;
  padding-right: 8px;
  user-select: none;
}
.log-row .txt {
  white-space: inherit;
}
/* 级别着色 */
.log-row.lv-critical .txt,
.log-row.lv-error .txt { color: #ff6b6b; }
.log-row.lv-warn .txt { color: #f8c555; }
.log-row.lv-info .txt { color: #60a5fa; }
.log-row.lv-debug .txt { color: #9ca3af; }
.pagination-container {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 10px;
}
.page-info {
  color: #666;
}
/* 统一按钮为蓝色(primary)。Naive UI 变量化主题，这里通过深度选择器限定当前组件范围 */
:deep(.n-button) {
  --n-color: #2080f0;
  --n-color-hover: #4098fc;
  --n-color-pressed: #1060c9;
  --n-color-focus: #2080f0;
  --n-text-color: #fff;
  --n-border: 1px solid #2080f0;
}
/* 非 primary 类型的按钮在本组件内也强制为蓝色外观 */
:deep(.n-button:not(.n-button--disabled)) {
  color: #fff;
  background-color: #2080f0;
  border-color: #2080f0;
}

/* 分析结果样式 */
.analysis-result {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  line-height: 1.6;
  max-height: 70vh;
  overflow-y: auto;
}

.analysis-result h1,
.analysis-result h2,
.analysis-result h3,
.analysis-result h4,
.analysis-result h5,
.analysis-result h6 {
  color: #2c3e50;
  margin-top: 24px;
  margin-bottom: 16px;
}

.analysis-result p {
  margin-bottom: 16px;
  color: #34495e;
}

.analysis-result ul,
.analysis-result ol {
  margin-bottom: 16px;
  padding-left: 24px;
}

.analysis-result li {
  margin-bottom: 8px;
}

.analysis-result code {
  background-color: #e9ecef;
  padding: 2px 6px;
  border-radius: 4px;
  font-family: 'Courier New', monospace;
}

.analysis-result pre {
  background-color: #2d3748;
  color: #e2e8f0;
  padding: 16px;
  border-radius: 8px;
  overflow-x: auto;
  margin: 16px 0;
}

.analysis-result blockquote {
  border-left: 4px solid #3b82f6;
  padding-left: 16px;
  margin: 16px 0;
  color: #6b7280;
  font-style: italic;
}

/* 文件信息样式 */
.file-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.file-name {
  font-weight: 500;
  color: #2c3e50;
}

.flex {
  display: flex;
}

.justify-between {
  justify-content: space-between;
}

.items-center {
  align-items: center;
}

.mb-4 {
  margin-bottom: 16px;
}

.ml-2 {
  margin-left: 8px;
}

/* 调试信息样式 */
.debug-info {
  background-color: #f0f8ff;
  border: 1px solid #87ceeb;
  border-radius: 4px;
  padding: 12px;
  margin-bottom: 12px;
  font-family: monospace;
  font-size: 12px;
  color: #2c3e50;
}

.debug-info p {
  margin: 4px 0;
}
</style>
